# 通过性分析控制器
import hashlib
import math
from io import BytesIO
from typing import Dict


from fastapi import APIRouter
from minio import S3Error
from pydantic import BaseModel
from seaman.core import JsonUtil

from common.method import *
from common.model import InterpolationData
from model.soft.soft_common import UniForceResult
from service.CompletionService import Completion
from service.DataPreService import read_base_data, read_interpolation_data
from service.InterpolationService import Interpolation
from common.params import ReqSoftParams, SoftParams

router = APIRouter()
class CasingParams(BaseModel):
    # 开始深度
    start: float
    # 结束深度
    end: float
    # 套管内径
    d: float

# L 大写，不然不好区分
class ReqPassParams(BaseModel):
    # 井号
    wellId: str
    # 插值长度
    space: float
    # 工具长度
    L1: float
    # 测井工具外径
    d1: float
    # 测井工具在空气中的重量 kg/m3
    m1: float
    # 流体密度
    rou_m: float
    # 工具摩擦系数
    mu1: float
    # # 测井仪器的单位浮重
    # w1: float
    # 套管列表
    casings: List[CasingParams]
    # 裸眼段内径Doh
    d_oh: float
    # 井下工具的下放速度v
    v_push: float
    # 井下工具的上提速度v
    v_pull: float
    # 裸眼段的深度L_OH
    L_oh: float
    # 液体的动力粘度
    tao_s: float
    # 仪器串第一最大直径
    d_max1: float
    # 仪器串第二最大直径
    d_max2: float
    # 电缆外径
    d2: float
    # 电缆在空气中的重量
    m2: float
    # 电缆摩擦系数
    mu2: float
    # 拉断力
    F_break: float
    # 张力计算参数
    F_weak_high: float
    F_weak_low: float
    F_weak_rate_low: float

class PassParams:
    # 井号
    wellId: str
    # 插值长度
    space: float
    # 工具长度
    L1: float
    # 测井工具外径
    d1: float
    # 测井工具在空气中的重量 kg/m3
    m1: float
    # 流体密度
    rou_m: float
    # 工具摩擦系数
    mu1: float
    # # 测井仪器的单位浮重
    # w1: float
    # 套管列表
    casings: List[CasingParams]
    # 裸眼段内径Doh
    d_oh: float
    # 井下工具的下放速度v
    v_push: float
    # 井下工具的上提速度v
    v_pull: float
    # 裸眼段的深度L_OH
    L_oh: float
    # 液体的动力粘度
    tao_s: float
    # 仪器串第一最大直径
    d_max1: float
    # 仪器串第二最大直径
    d_max2: float
    # 电缆外径
    d2: float
    # 电缆在空气中的重量
    m2: float
    # 电缆摩擦系数
    mu2: float
    # 拉断力
    F_break: float
    # 张力计算参数
    F_weak_high: float
    F_weak_low: float
    F_weak_rate_low: float

    def __init__(self, params: ReqPassParams):
        self.wellId = params.wellId
        self.space = params.space
        self.L1 = round(params.L1)
        self.d1 = params.d1
        self.m1 = params.m1
        self.rou_m = params.rou_m
        self.mu1 = params.mu1
        self.casings = params.casings
        self.d_oh = params.d_oh
        self.v_push = params.v_push
        self.v_pull = params.v_pull
        self.L_oh = params.L_oh
        self.tao_s = params.tao_s
        self.tao_p = get_conversion_viscosity(params)
        self.d_max1 = params.d_max1
        self.d_max2 = params.d_max2
        self.d2 = params.d2
        self.m2 = params.m2
        self.mu2 = params.mu2
        self.F_break = params.F_break
        self.F_weak_high = params.F_weak_high
        self.F_weak_low = params.F_weak_low
        self.F_weak_rate_low = params.F_weak_rate_low
        self.w1 = get_instrument_unit_weight(params)

    # 测井仪器截面面积
    def get_instrument_area(params) -> float:
        return math.pi * 0.25 * math.pow(params.d1, 2)

    # 测井仪器的单位密度
    def get_instrument_uint_density(params) -> float:
        return params.m1 / get_instrument_area(params)

    # 测井仪器的单位浮重
    def get_instrument_unit_weight(params) -> float:
        return (get_instrument_uint_density(params) - params.rou_m) * get_instrument_area(params) * 9.81

    # 马氏漏斗粘度转换
    def get_conversion_viscosity(params) -> float:
        factor_a: float = math.pow(10, -3)
        factor_b: float = math.log((params.tao_s - 24.5) / 0.58) / 1.2
        factor_c: float = math.log(params.rou_m * factor_a)
        viscosity: float = factor_a * math.pow(math.e, factor_b + factor_c)
        return viscosity

    def calc_md5(self):
        # 将类中数据项的值连接成一个字符串
        data_string = f"{self.wellId}{self.space}{self.L1}{self.d1}{self.d2}{self.m1}{self.m2}{self.mu1}{self.mu2}{self.F_weak_low}{self.F_weak_high}{self.F_weak_rate_low}{self.F_break}"

        # 计算 MD5 哈希
        md5_hash = hashlib.md5(data_string.encode()).hexdigest()

        return md5_hash




# 几何通过性分析在 ctl 中实现
@router.post("/v3/geometry/passcheck")
async def geometr_pass_check(req_params: ReqSoftParams) :
    params: SoftParams = SoftParams(req_params)
    # 获取井信息
    wellId = params.wellId
    # 插值
    space: float = params.space
    # 获取是否有造斜点
    is_slanting_point = params.is_slanting_point
    # 根据井名称获取基础数据项
    interpolation_data_list: List[InterpolationData] = read_interpolation_data(wellId, space, is_slanting_point)
    # 获取套管信息列表
    casings: List[CasingParams] = params.casings
    # 获取仪器信息
    L1: float = params.L1
    # 定义通过困难列表
    pass_hard_depth: List[float] = []
    # 定义可通过最大仪器长度
    max_pass_len: float = params.L1
    max_hard_depth: float = 0
    # 获取插值计算信息 并遍历
    for i, item in enumerate(interpolation_data_list):
        depth: float = item.depth
        # 大于裸研段 break
        if depth > params.L_oh:
            break
        # 曲率半径
        R: float = item.curvature_radius
        # 是否处于套管范围
        in_casing: bool = False
        # 如果不是最后一个 item，则处理
        if i < len(interpolation_data_list) - 1:
            # 在这里可以访问前一个值pre_item当前 item 和下一个 item 的值
            if item == 0.0:
                pre_item = item
            else:
                pre_item = interpolation_data_list[i - 1]

            next_item = interpolation_data_list[i + 1]
        # 判断当前深度处于什么段位
        for c_item in casings:
            # 处于当前套管段
            if depth >= c_item.start and depth <= c_item.end:
                # 设置状态为 True
                in_casing = True

                # 套管内径
                D: float = c_item.d
                # 定义计算元
                factor_a: float = math.pow(R + D, 2)
                factor_b: float = math.pow(R + params.d_max1/2 + params.d_max2/2, 2)
                result: float = 2 * math.sqrt(factor_a - factor_b)
                # print("深度{}可通过最大长度{}".format(depth, result))
                # 通过困难点
                if result < params.L1 and depth > 0 and item.inclination != next_item.inclination and item.azimuth != next_item.azimuth and item.inclination != pre_item.inclination and item.azimuth != pre_item.azimuth:
                    if result < max_pass_len:
                        max_pass_len = result
                        max_hard_depth = depth
                    pass_hard_depth.append(depth)

        # 不在套管范围 -> 裸段
        if in_casing == False:
            # D 为
            D: float = params.d_oh
            # 定义计算元
            factor_a: float = math.pow(R + D, 2)
            factor_b: float = math.pow(R + params.d_max1 / 2 + params.d_max2 / 2, 2)
            result: float = 2 * math.sqrt(factor_a - factor_b)
            # 通过困难点
            if result < params.L1 and item.inclination != next_item.inclination and item.azimuth != next_item.azimuth and item.inclination != pre_item.inclination and item.azimuth != pre_item.azimuth:
                if result < max_pass_len:
                    max_pass_len = result
                    max_hard_depth = depth
                pass_hard_depth.append(depth)



    # 是否存在通过困难点
    has_hard: bool = False
    first_hard_depth: float = 0.0
    if len(pass_hard_depth) > 0:
        has_hard = True
        first_hard_depth = pass_hard_depth[0]

    return {
        # 是否存在几何通过困难点
        'has_hard':has_hard,
        # 仪器长度
        'L1': params.L1,
        # 第一个几何通过困难点
        'first_hard_depth': first_hard_depth,
        # 可通过的最大几何长度
        'max_pass_len': round(max_pass_len,2),
        # 几何通过性最困难的深度
        'max_hard_depth': max_hard_depth,
        # 几何通过困难点列表
        'pass_hard_depth': pass_hard_depth
    }


from model.soft.cable_tension import run as cable_run

@router.post("/v3/mechanics/passcheck")
async def mechanics_pass_check(req_params: ReqSoftParams):

    params: SoftParams = SoftParams(req_params)
    # 计算 MD5 哈希值
    md5_hash = req_params.calc_md5()

    params.L1 = params.L1
    # 获取井信息
    wellId = params.wellId
    # 插值
    space: float = params.space
    # 获取是否有造斜点
    is_slanting_point = params.is_slanting_point
    # 构建参数对象
    data_object_name = wellId + '/' + md5_hash + '-mechanics.xlsx'

    # 计算 MD5 哈希值
    soft_params: SoftParams = SoftParams(None)
    soft_params.buildByPassParams(params)
    # 获取插值数据
    _list_: List[InterpolationData] = read_interpolation_data(wellId, space, is_slanting_point)
    inter_data_list = _list_
    # _list_ = get_test_data()
    # 计算的间距
    # calculate_space = params.calculate_space
    calculate_space = 1.0

    last_depth = _list_[-1].depth
    # 设置间隔深度列表
    desired_depths = [last_depth]
    # 从最后一个深度开始，减去间距，直到小于间距为止 这样就只计算部分深度
    while desired_depths[-1] > calculate_space:
        new_value = desired_depths[-1] - calculate_space
        desired_depths.append(new_value)
        # 对列表进行排序并去除重复值
    desired_depths = sorted(set(desired_depths), reverse=True)

    # 是否加入液体阻力 @TODO 如果为true就加入 否则加0
    is_Not_add_resistance = params.add_liquid_resistance
    result: List = cable_run(soft_params, _list_, is_Not_add_resistance, desired_depths)
    cable_tension_list = result
    cable_tension_list.reverse()
    # 读取插值信息 -> 固定 1.0
    # 构建 inc dict
    # 获取给定的井斜角的值

    inc_dict: Dict = {}
    for inter_item in inter_data_list:
        inc_dict[inter_item.depth] = inter_item.inclination
        # print(inter_item.depth, inter_item.inclination)

    # 获取套管信息列表
    casings: List[CasingParams] = params.casings
    # 定义词典
    f_s_dict = {}
    # 定义 n_sum_dict
    n_sum_dict = {}
    # 获取插值计算信息 并遍历
    for item in cable_tension_list:
        depth: float = item.depth
        n_sum_dict[depth] = item.N_sum
        # 大于裸研段 break
        if depth > params.L_oh:
            break
        # 是否范围内
        in_casing: bool = False
        # 计算 Fs
        for c_item in casings:
            # 处于套管范围内
            if depth >= c_item.start and depth <= c_item.end:
                in_casing = True
                rate: float = c_item.d / params.d_max1
                # 定义计算元
                factor_a: float = 2 * math.pi * params.tao_p * round(params.L1) * params.v_push
                factor_b: float = math.pow(rate, 2) - 1
                factor_c: float = (math.pow(rate,2) + 1) * math.log(rate)
                # 计算结果
                res: float = (factor_a * factor_b) / (factor_c - factor_b)
                # 存入 fs
                f_s_dict[depth] = res
                # print('深度{}处于套管内，值为:{}'.format(depth, res))
        # 不在套管范围内
        if in_casing == False:
            rate: float = params.d_oh / params.d_max1
            # 定义计算元
            factor_a: float = 2 * math.pi * params.tao_p * round(params.L1) * params.v_push
            factor_b: float = math.pow(rate, 2) - 1
            factor_c: float = (math.pow(rate, 2) + 1) * math.log(rate)
            # 计算结果
            res: float = (factor_a * factor_b) / (factor_c - factor_b)
            f_s_dict[depth] = res
            # print('深度{}处于套管外，值为:{}'.format(depth, res))
        # print("深度{}液体阻力为:{}".format(depth,f_s_dict[depth]))
    # 反转
    # for itm in cable_tension_list:
    #     print(JsonUtil.obj2str(itm))
    # 已经知道了 f_s_dict
    # @TODO 现在不在使用这个累加液体阻力了 使用单个的 这块不删除 备用
    # f_s_all_dict = {}
    # # 初始化
    # for item in cable_tension_list:
    #     depth: float = item.depth
    #     # print("f_s_dict计算深度:", depth)
    #     if depth <= round(params.L1):
    #         f_s_all_dict[depth] = 0
    #     else:
    #         f_s_all_dict[depth] = f_s_all_dict[depth - 1.0] + f_s_dict[depth]
        # print("深度{}累计液体阻力为:{}".format(depth, f_s_all_dict[depth]))

    # 计算 f_f_d
    # 摩阻力
    f_f_dict = {}
    for item in cable_tension_list:
        depth: float = item.depth
        if depth >= round(params.L1):
            # @TODO 这里修改之后应该是加上 f_s_dict[depth] 而不是 f_s_all_dict[depth]
            # f_f_dict[depth] = params.mu1 * n_sum_dict[depth] + f_s_all_dict[depth]
            f_f_dict[depth] = params.mu1 * n_sum_dict[depth] + f_s_dict[depth]

    # 计算 f_r_d
    f_r_dict = {}
    result_arr = []


    # 定义通过困难点
    pass_hard_depth: List[float] = []
    for item in cable_tension_list:
        depth: float = item.depth
        # print(depth)
        if depth >= round(params.L1):
            factor_cos = inc_dict[depth] - inc_dict[depth - round(params.L1)]
            res: float = params.w1 * round(params.L1) * math.cos(math.radians(factor_cos)) - f_f_dict[depth]
            f_r_dict[depth] = res
            obj = {}
            obj['深度'] = depth
            obj['液体阻力(N)'] = f_s_dict[depth]
            # obj['累计液体阻力(N)'] = f_s_all_dict[depth]
            obj['总摩阻力(N)'] = f_f_dict[depth]
            obj['轴向力'] = f_r_dict[depth]
            obj['通过性'] = 1
            if res <= 0:
                obj['通过性'] = 2
                pass_hard_depth.append(depth)
            result_arr.append(obj)
    df = pd.DataFrame(result_arr)
    # 生成并上传Excel数据
    excel_data = df
    # 将数据写入Excel文件
    excel_file = io.BytesIO()
    excel_data.to_excel(excel_file, index=False)
    excel_file.seek(0)
    # putObject('well', data_object_name, excel_file)
    # 是否存在通过困难点
    has_hard: bool = False
    first_hard_depth: float = 0.0
    if len(pass_hard_depth) > 0:
        has_hard = True
        first_hard_depth = pass_hard_depth[0]
    # 返回结果
    return {
        # 是否存力学通过困难点
        'has_hard': has_hard,
        # 仪器长度
        'L1': params.L1,
        # 第一个力学通过困难点
        'first_hard_depth': first_hard_depth,
        # 力学通过困难点列表
        'pass_hard_depth': pass_hard_depth
    }
